
package Ajedrez;

import Herramientas.Conversor;
import Herramientas.Memento;
import Herramientas.Tarea;
import Prolog.ComunicadorProlog;
import java.io.IOException;
import javax.swing.JPanel;
import Main.BotonCasilla;
import Main.VentanaAjedrez;

/**
 *
 * @author Carlos
 */
public class Ajedrez extends JPanel {

    
        /*
     isInSquare(0, blackKing(1), d, 1).
isInSquare(0, whiteKing(1), c, 4).
isInSquare(0, whiteBishop(1), e, 3).
isInSquare(0, whiteBishop(2), d, 3).

     */
        /***** Constantes *****/
    public String fichaInicial[][] = {
        //{"Ntorre", "Ncaballo", "Nalfil", "Nreina", "Nrey", "Nalfil", "Ncaballo", "Ntorre"},
        {"blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco"},
        {"blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco"},
        {"blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco"},
        {"blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco"},
        {"blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco"},
        {"blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco"},
        {"blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco"},
        //{"torre", "caballo", "alfil", "reina", "rey", "alfil", "caballo", "torre"},};
        {"blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco", "blanco"},};
    /**
     * Jugador de blancas
     */
    public static final int blancas = 1;
    /**
     * Jugador de negras
     */
    public static final int negras = 0;
    /**
     * Casilla en blanco
     */
    public static final int blanco = 0;
    //Piezas blancas
    public static final int peon = 1;
    public static final int alfil = 2;
    public static final int caballo = 3;
    public static final int torre = 4;
    public static final int reina = 5;
    public static final int rey = 6;
    //Piezas negras
    public static final int Npeon = -1;
    public static final int Nalfil = -2;
    public static final int Ncaballo = -3;
    public static final int Ntorre = -4;
    public static final int Nreina = -5;
    public static final int Nrey = -6;
    /**** Variables ****/
    private int numAlfB = 0;
    private int numCabB = 0;
    private int numPeonB = 0;
    private int numReinaB = 0;
    private int numTorresB = 0;

    private int numAlfN = 0;
    private int numCabN = 0;
    private int numPeonN = 0;
    private int numReinaN = 0;
    private int numTorresN = 0;

    private ComunicadorProlog comProlog;

    /** Botones de la interfaz que representan las casillas. */
    public BotonCasilla ficha[][] = new BotonCasilla[8][8];
    private String turno;
    Movimiento movActual = new Movimiento();
    // Flag indica si se ha pulsado un botón que implica movimiento. Si es false quiere decir que ha sido una selección de pieza.
    public boolean flag;
    private int jugador;//"blancas" = 1 ; "negras" = 0
    /* Colleges (Mediator)*/
    private VentanaAjedrez va;
    private Memento mem;


    /*********************** Métodos ***********************************/
    /**
     * Constructor.
     * Es posible indicar con qué partes trabajar.
     */
    public Ajedrez() {
        this.flag = false;
        this.turno = "s";
        this.jugador = blancas;
        comProlog = new ComunicadorProlog(this);
    }

    /**
     * Inicializa la ventana y las conexiones que se han especificado en el constructor
     */
    public void iniciarAjedrez() throws IOException{

        this.mem = new Memento();
        this.va = new VentanaAjedrez(this);
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                va.setVisible(true);
            }
        });

//        nuevaPartida();

    }

    /* Coloca las piezas en el estado inicial */
    private void ponerPiezasEnBotones() {
        enviarMovimientoAProlog("isInSquare(0,whiteKing(1),h,4)");
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                this.ficha[i][j].setPieza(fichaInicial[i][j]);
                this.ficha[i][j].setNombre(getPiezaParaProlog(fichaInicial[i][j]));
                if(!this.ficha[i][j].getNombre().equalsIgnoreCase(""))
                    enviarMovimientoAProlog("assert(isInSquare(0,"+this.ficha[i][j].getNombre()+","+Conversor.intToLetraCol(j)+","+(8-i)+")).");
            }
        }
        enviarMovimientoAProlog("isInSquare(0,whiteBishop(1),c,1)");
        enviarMovimientoAProlog("isInSquare(0,whiteBishop(1),a,1)");
        this.repintarImagenes();
        mem.guardarEstado(this);
    }

    /**
     * Prepara el tablero para una nueva partida y avisa a crafty de ello.
     */
    public synchronized void nuevaPartida() {
        // Si habia una pieza seleccionada, se deselecciona:
        if (this.getTurno().equalsIgnoreCase("p")) {
            this.setTurno("s");
            this.desseleccionarFicha();
        }


        ponerPiezasEnBotones();
        this.repintarImagenes();
        if (va != null) {
            va.resetTextArea();
        }

        this.setJugador(blancas);
        this.setTurno("s");
        
        numAlfB = 0;
        numCabB = 0;
        numPeonB = 0;
        numReinaB = 0;
        numTorresB = 0;

        numAlfN = 0;
        numCabN = 0;
        numPeonN = 0;
        numReinaN = 0;
        numTorresN = 0;
        this.repintarImagenes();
        this.enviarMovimientoAProlog("play.");
        this.consultarMovimientoAProlog();        
    }

    /** Método importante. Tiene 2 funciones:

     * - Si no hay ninguna pieza seleccionada, se selecciona la que esté en la posición de los parámetros

     * - Si hay una pieza seleccionada, los parámetros indican el destino de la pieza y se realiza el movimiento.

     */
    public String Turno(Posicion pos) {
        System.out.println("Posicion: "+pos);
        String s = "";

        //Principio. Selección de pieza a mover
        if ((!this.turno.equalsIgnoreCase("p")) && esPiezaTuya(this.ficha[pos.getPosX()][pos.getPosY()].getPieza())) {
            this.movActual = new Movimiento();
            seleccionarFicha(pos);
            this.setTurno("p");
            this.flag = false;
        } 
        // Si se picha dos veces en la misma pieza, ésta se desselecciona
        else if (movActual.getOrigen() != null && movActual.getOrigen().getPosX() == pos.getPosX() && movActual.getOrigen().getPosY() == pos.getPosY()) {
            this.desseleccionarFicha();
            movActual = new Movimiento();
            this.setTurno("s");
        } 
        //Movimiento. Ya conocemos desde dónde.
        else if (this.turno.equalsIgnoreCase("p")) {
//            System.out.println("Movimiento de " + iniX + "-" + iniY + " a " + posX + "-" + posY);
            this.setTurno("s");
            movActual.setDestino(pos);
            boolean movOK = false;
            int iniX = movActual.getOrigen().getPosX();
            int iniY = movActual.getOrigen().getPosY();

            //Comprobamos que no se ataca a ninguna pieza del mismo color
            Validar vv = new Validar(this.ficha, movActual);
            //Comprobamos que el movimiento de la pieza es correcto
            Pieza p = crearPieza(this.ficha[iniX][iniY].getPieza(), movActual);
//            if (vv.getcorrept() && p.getcorrept()) {
                movOK = true;
  //          }

            /* Fin comprobación de movimientos */
            if (movOK) {
                s = realizarMovimiento(movActual);

                if (va != null) {
                    va.cambiarTextArea(s);
                }
                cambiarJugador();
                this.flag = true;
                mem.guardarEstado(this);
            } else {
                movNoValido();
            }
        }

        /* Sirve para volver a pintar las imágenes */
        this.repintarImagenes();
        return s;
    }

    /* Crea la sentencia que se enviará a crafty como movimiento realizado.
     * También se muestra en la ventana del desarrollo de la partida.
     *
     * No se pone si se está dando Jaque...
     */
    private String realizarMovimiento(Movimiento mov) {
        int iniX = mov.getOrigen().getPosX();
        int iniY = mov.getOrigen().getPosY();
        int posX = mov.getDestino().getPosX();
        int posY = mov.getDestino().getPosY();
        String s = "playPosition(";
        String orig = this.ficha[iniX][iniY].getPieza();
        this.ficha[posX][posY].setPieza(orig);
        this.ficha[posX][posY].setNombre(this.ficha[iniX][iniY].getNombre());
        s += this.ficha[iniX][iniY].getNombre();
        this.ficha[iniX][iniY].setPieza("blanco");
        this.ficha[iniX][iniY].setNombre("");
        String piezaLetra = this.ficha[iniX][iniY].getNombre();
        s += piezaLetra + "," + Conversor.intToLetraCol(posY) + "," + (8 - posX) + ")";

        this.ficha[iniX][iniY].desmarcarCasilla();
        return s;
    }

    /*
    Avisa de que el movimiento que se ha intentado no es posible.
    Se deselecciona la casilla que estuviese seleccionada.
     */
    private void movNoValido() {
        desseleccionarFicha();
        informar(Tarea.MOVIMIENTO_INVALIDO);
        //javax.swing.JOptionPane.showMessageDialog(null, "Movimiento no valido", "", javax.swing.JOptionPane.ERROR_MESSAGE);

    }

    private void seleccionarFicha(Posicion p) {
        movActual.setOrigen(p);
        this.ficha[p.getPosX()][p.getPosY()].marcarCasilla();
    }

    private void desseleccionarFicha() {
        Posicion p = movActual.getOrigen();
        if (p != null) {
            this.ficha[p.getPosX()][p.getPosY()].desmarcarCasilla();
        }

        movActual = new Movimiento();
    }

    /**
     * Cambia el turno de movimiento
     */
    private void cambiarJugador() {
        if (this.getJugador() == blancas) {
            this.setJugador(negras);
        } else {
            this.setJugador(blancas);
        }
        this.flag = true;
    }

    /*
     * Indica si la pieza es del jugador al que le toca mover
     */
    private boolean esPiezaTuya(String pieza) {
        if (this.getJugador() == blancas) {
            return Conversor.piezaInt(pieza) > 0;
        } else {
            return Conversor.piezaInt(pieza) < 0;
        }
    }

    /**
     * A partir de un String se crea la pieza correspondiente a dicho String. Sirve para calcular la
     * validez del movimiento sobre la pieza y no sobre cada tipo individualmente.
     */
    private Pieza crearPieza(String pieza, Movimiento mov) {
        int iniX = mov.getOrigen().getPosX();
        int iniY = mov.getOrigen().getPosY();
        int finX = mov.getDestino().getPosX();
        int finY = mov.getDestino().getPosY();
        if (pieza.equalsIgnoreCase("alfil") || pieza.equalsIgnoreCase("Nalfil")) {
            return new Alfil(iniX, iniY, finX, finY);
        }

        if (pieza.equalsIgnoreCase("caballo") || pieza.equalsIgnoreCase("Ncaballo")) {
            return new Caballo(iniX, iniY, finX, finY);
        }

        if (pieza.equalsIgnoreCase("reina") || pieza.equalsIgnoreCase("Nreina")) {
            return new Reina(iniX, iniY, finX, finY);
        }

        if (pieza.equalsIgnoreCase("peon") || pieza.equalsIgnoreCase("Npeon")) {
            return new Peon(ficha, iniX, iniY, finX, finY);
        }

        if (pieza.equalsIgnoreCase("torre") || pieza.equalsIgnoreCase("Ntorre")) {
            return new Torre(iniX, iniY, finX, finY);
        }

        if (pieza.equalsIgnoreCase("rey")) {
            return new Rey(iniX, iniY, finX, finY, false, false, true);
        }

        if (pieza.equalsIgnoreCase("Nrey")) {
            return new Rey(iniX, iniY, finX, finY, false, false, false);
        }

        return null;
    }

    
    public String getPiezaParaProlog(String pieza){
        if (pieza.equalsIgnoreCase("alfil")) {
            this.numAlfB++;
            return "whiteBishop("+this.numAlfB+")";
        }
        if (pieza.equalsIgnoreCase("Nalfil")) {
            this.numAlfN++;
            return "blackBishop("+this.numAlfN+")";
        }

        if (pieza.equalsIgnoreCase("caballo")) {
            this.numCabB++;
            return "whiteKnight("+this.numCabB+")";
        }
        if (pieza.equalsIgnoreCase("Ncaballo")) {
            this.numCabN++;
            return "blackKnight("+this.numCabN+")";
        }

        if (pieza.equalsIgnoreCase("reina")) {
            this.numReinaB++;
            return "whiteQueen("+this.numReinaB+")";
        }
        if (pieza.equalsIgnoreCase("Nreina")) {
            this.numReinaN++;
            return "blackQueen("+this.numReinaN+")";
        }

        if (pieza.equalsIgnoreCase("peon")) {
            this.numPeonB++;
            return "whitePawn("+this.numPeonB+")";
        }
        if (pieza.equalsIgnoreCase("Npeon")) {
            this.numPeonN++;
            return "blackPawn("+this.numPeonN+")";
        }

        if (pieza.equalsIgnoreCase("torre")) {
            this.numTorresB++;
            return "whiteRook("+this.numTorresB+")";
        }
        if (pieza.equalsIgnoreCase("Ntorre")) {
            this.numTorresN++;
            return "blackRook("+this.numTorresN+")";
        }

        if (pieza.equalsIgnoreCase("rey")) {
            return "whiteKing(1)";
        }

        if (pieza.equalsIgnoreCase("Nrey")) {
            return "blackKing(1)";
        }

        return "";        
    }
    
    
    /**
     * Pinta los coleres correspondientes a cada casilla y la pieza si la tuviera.
     */
    public void repintarImagenes() {
        for (int X = 0; X < 8; X++) {
            for (int Y = 0; Y < 8; Y++) {
                ficha[X][Y].pintarBoton();
            }
        }
    }

    /**
     * Pone en el área de texto de la ventana dedicada para tareas lo que se le pase.
     */
    public void informar(int inf) {
        this.va.informarDeTarea(Tarea.informar(inf));
    }

    /**
     * Pone en el área de texto de la ventana dedicada para tareas lo que se le pase.
     */
    public void informar(int inf, String adicional) {
        this.va.informarDeTarea(Tarea.informar(inf) + " " + adicional);
    }

    
    /* Envía a Prolog el mensaje que se haya elegido en la interfaz */
    public boolean enviarMovimientoAProlog(String mensaje) {
        if (comProlog != null) {
            return comProlog.enviarMovimiento(mensaje);
        }
        return false;
    }
    
    /* Envía a Prolog el mensaje que se haya elegido en la interfaz */
    public void consultarMovimientoAProlog() {
        if (comProlog != null) {
            comProlog.consultarMovimiento();
        }
    }
    

    public Posicion buscarPiezaEnTablero(String pieza){
        for (int i = 0; i < 8; i++) {
            for (int j = 0; j < 8; j++) {
                String s = this.ficha[i][j].getNombre();
                if(s.equalsIgnoreCase(pieza))
                    return new Posicion(i, j);
            }
        }
        return null;
    }
    
    public void deshacerMovimiento(){
        mem.cargarEstadoAnterior(this);
        this.cambiarJugador();
        this.desseleccionarFicha();
    }

    /******************* Getters y Setters **********************/

    public BotonCasilla[][] getFicha() {
        return ficha;
    }

    public void setFicha(BotonCasilla[][] ficha) {
        this.ficha = ficha;
    }

    public String[][] getfichaInicial() {
        return fichaInicial;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public int getJugador() {
        return jugador;
    }

    public void setJugador(int jugador) {
        this.jugador = jugador;
    }

    public String getTurno() {
        return turno;
    }

    public void setTurno(String turno) {
        this.turno = turno;
    }

    /* Asigna la ventana al ajedrez (Patrón mediator)*/
    public void setVentana(VentanaAjedrez va) {
        this.va = va;
    }
    
}
